/*
 NokiaLCD.cpp - Library for a Nokia LCD with the epson driver.
 Created by Thomas Jespersen, July 2009 (Originally Arduino Sketch by Gravitech.us)
 Released into the public domain.
 
 Interface LCD6610 to Arduino Nano
 Nano --> LCD6610
 D2 --> BL
 D3 --> #CS
 D4 --> SCLK
 D5 --> SDATA
 D6 --> #RESEET
 +5V --> VCC,VBL
 GND --> GND
 */

//#include "WProgram.h"
#include "NokiaLCD.h"

// Define Software SPI Pin Signal

#define BL 2 // Digital 2 --> BL
#define CS 4 // Digital 3 --> #CS
#define CLK 7 // Digital 4 --> SCLK
#define SDA 5 // Digital 5 --> SDATA
#define RESET 6 // Digital 6 --> #RESET

// Basic Colors
#define WHITE 0xFFF
#define BLACK 0x000
#define RED 0xF00
#define GREEN 0x0F0
#define BLUE 0x00F
#define CYAN 0x0FF
#define MAGENTA 0xF0F
#define YELLOW 0xFF0
#define BROWN 0xB22
#define ORANGE 0xFA0
#define PINK 0xF6A

// Epson S1D15G10 Command Set
#define DISON 0xaf
#define DISOFF 0xae
#define DISNOR 0xa6
#define DISINV 0xa7
#define COMSCN 0xbb
#define DISCTL 0xca
#define SLPIN 0x95
#define SLPOUT 0x94
#define PASET 0x75
#define CASET 0x15
#define DATCTL 0xbc
#define RGBSET8 0xce
#define RAMWR 0x5c
#define RAMRD 0x5d
#define PTLIN 0xa8
#define PTLOUT 0xa9
#define RMWIN 0xe0
#define RMWOUT 0xee
#define ASCSET 0xaa
#define SCSTART 0xab
#define OSCON 0xd1
#define OSCOFF 0xd2
#define PWRCTR 0x20
#define VOLCTR 0x81
#define VOLUP 0xd6
#define VOLDOWN 0xd7
#define TMPGRD 0x82
#define EPCTIN 0xcd
#define EPCOUT 0xcc
#define EPMWR 0xfc
#define EPMRD 0xfd
#define EPSRRD1 0x7c
#define EPSRRD2 0x7d
#define NOP 0x25

#define cbi(reg, bit) (reg&=~(1<<bit))
#define sbi(reg, bit) (reg|= (1<<bit))

#define CS0 cbi(PORTB,CS);
#define CS1 sbi(PORTB,CS);
#define CLK0 cbi(PORTB,CLK);
#define CLK1 sbi(PORTB,CLK);
#define SDA0 cbi(PORTB,SDA);
#define SDA1 sbi(PORTB,SDA);
#define RESET0 cbi(PORTD,RESET);
#define RESET1 sbi(PORTD,RESET);
#define BL0 cbi(PORTD,BL);
#define BL1 sbi(PORTD,BL);

byte const font[475] = {
0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x06 , 0x5F , 0x06 , 0x00 , // !
0x07 , 0x03 , 0x00 , 0x07 , 0x03 , // ,
0x24 , 0x7E , 0x24 , 0x7E , 0x24 , // #
0x24 , 0x2B , 0x6A , 0x12 , 0x00 , // $
0x63 , 0x13 , 0x08 , 0x64 , 0x63 , // %
0x36 , 0x49 , 0x56 , 0x20 , 0x50 , // &
0x00 , 0x07 , 0x03 , 0x00 , 0x00 , // //
0x00 , 0x3E , 0x41 , 0x00 , 0x00 , // (
0x00 , 0x41 , 0x3E , 0x00 , 0x00 , // )
0x08 , 0x3E , 0x1C , 0x3E , 0x08 , // *
0x08 , 0x08 , 0x3E , 0x08 , 0x08 , // +
0x00 , 0xE0 , 0x60 , 0x00 , 0x00 , // ,
0x08 , 0x08 , 0x08 , 0x08 , 0x08 , // -
0x00 , 0x60 , 0x60 , 0x00 , 0x00 , // .
0x20 , 0x10 , 0x08 , 0x04 , 0x02 , // /
0x3E , 0x51 , 0x49 , 0x45 , 0x3E , // 0
0x00 , 0x42 , 0x7F , 0x40 , 0x00 , // 1
0x62 , 0x51 , 0x49 , 0x49 , 0x46 , // 2
0x22 , 0x49 , 0x49 , 0x49 , 0x36 , // 3
0x18 , 0x14 , 0x12 , 0x7F , 0x10 , // 4
0x2F , 0x49 , 0x49 , 0x49 , 0x31 , // 5
0x3C , 0x4A , 0x49 , 0x49 , 0x30 , // 6
0x01 , 0x71 , 0x09 , 0x05 , 0x03 , // 7
0x36 , 0x49 , 0x49 , 0x49 , 0x36 , // 8
0x06 , 0x49 , 0x49 , 0x29 , 0x1E , // 9
0x00 , 0x6C , 0x6C , 0x00 , 0x00 , // :
0x00 , 0xEC , 0x6C , 0x00 , 0x00 , // ;
0x08 , 0x14 , 0x22 , 0x41 , 0x00 , // <
0x24 , 0x24 , 0x24 , 0x24 , 0x24 , // =
0x00 , 0x41 , 0x22 , 0x14 , 0x08 , // >
0x02 , 0x01 , 0x59 , 0x09 , 0x06 , // ?
0x3E , 0x41 , 0x5D , 0x55 , 0x1E , // @
0x7E , 0x09 , 0x09 , 0x09 , 0x7E , // A
0x7F , 0x49 , 0x49 , 0x49 , 0x36 , // B
0x3E , 0x41 , 0x41 , 0x41 , 0x22 , // C
0x7F , 0x41 , 0x41 , 0x41 , 0x3E , // D
0x7F , 0x49 , 0x49 , 0x49 , 0x41 , // E
0x7F , 0x09 , 0x09 , 0x09 , 0x01 , // F
0x3E , 0x41 , 0x49 , 0x49 , 0x7A , // G
0x7F , 0x08 , 0x08 , 0x08 , 0x7F , // H
0x00 , 0x41 , 0x7F , 0x41 , 0x00 , // I
0x30 , 0x40 , 0x40 , 0x40 , 0x3F , // J
0x7F , 0x08 , 0x14 , 0x22 , 0x41 , // K
0x7F , 0x40 , 0x40 , 0x40 , 0x40 , // L
0x7F , 0x02 , 0x04 , 0x02 , 0x7F , // M
0x7F , 0x02 , 0x04 , 0x08 , 0x7F , // N
0x3E , 0x41 , 0x41 , 0x41 , 0x3E , // O
0x7F , 0x09 , 0x09 , 0x09 , 0x06 , // P
0x3E , 0x41 , 0x51 , 0x21 , 0x5E , // Q
0x7F , 0x09 , 0x09 , 0x19 , 0x66 , // R
0x26 , 0x49 , 0x49 , 0x49 , 0x32 , // S
0x01 , 0x01 , 0x7F , 0x01 , 0x01 , // T
0x3F , 0x40 , 0x40 , 0x40 , 0x3F , // U
0x1F , 0x20 , 0x40 , 0x20 , 0x1F , // V
0x3F , 0x40 , 0x3C , 0x40 , 0x3F , // W
0x63 , 0x14 , 0x08 , 0x14 , 0x63 , // X
0x07 , 0x08 , 0x70 , 0x08 , 0x07 , // Y
0x71 , 0x49 , 0x45 , 0x43 , 0x00 , // Z
0x00 , 0x7F , 0x41 , 0x41 , 0x00 , // [
0x02 , 0x04 , 0x08 , 0x10 , 0x20 , // Back slash
0x00 , 0x41 , 0x41 , 0x7F , 0x00 , // ]
0x04 , 0x02 , 0x01 , 0x02 , 0x04 , // ^
0x80 , 0x80 , 0x80 , 0x80 , 0x80 , // _
0x00 , 0x03 , 0x07 , 0x00 , 0x00 , // `
0x20 , 0x54 , 0x54 , 0x54 , 0x78 , // a
0x7F , 0x44 , 0x44 , 0x44 , 0x38 , // b
0x38 , 0x44 , 0x44 , 0x44 , 0x28 , // c
0x38 , 0x44 , 0x44 , 0x44 , 0x7F , // d
0x38 , 0x54 , 0x54 , 0x54 , 0x18 , // e
0x08 , 0x7E , 0x09 , 0x09 , 0x00 , // f
0x18 , 0xA4 , 0xA4 , 0xA4 , 0x7C , // g
0x7F , 0x04 , 0x04 , 0x78 , 0x00 , // h
0x00 , 0x00 , 0x7D , 0x00 , 0x00 , // i
0x40 , 0x80 , 0x84 , 0x7D , 0x00 , // j
0x7F , 0x10 , 0x28 , 0x44 , 0x00 , // k
0x00 , 0x00 , 0x7F , 0x40 , 0x00 , // l
0x7C , 0x04 , 0x18 , 0x04 , 0x78 , // m
0x7C , 0x04 , 0x04 , 0x78 , 0x00 , // n
0x38 , 0x44 , 0x44 , 0x44 , 0x38 , // o
0xFC , 0x44 , 0x44 , 0x44 , 0x38 , // p
0x38 , 0x44 , 0x44 , 0x44 , 0xFC , // q
0x44 , 0x78 , 0x44 , 0x04 , 0x08 , // r
0x08 , 0x54 , 0x54 , 0x54 , 0x20 , // s
0x04 , 0x3E , 0x44 , 0x24 , 0x00 , // t
0x3C , 0x40 , 0x20 , 0x7C , 0x00 , // u
0x1C , 0x20 , 0x40 , 0x20 , 0x1C , // v
0x3C , 0x60 , 0x30 , 0x60 , 0x3C , // w
0x6C , 0x10 , 0x10 , 0x6C , 0x00 , // x
0x9C , 0xA0 , 0x60 , 0x3C , 0x00 , // y
0x64 , 0x54 , 0x54 , 0x4C , 0x00 , // z
0x08 , 0x3E , 0x41 , 0x41 , 0x00 , // {
0x00 , 0x00 , 0x7F , 0x00 , 0x00 , // |
0x00 , 0x41 , 0x41 , 0x3E , 0x08 , // }
0x02 , 0x01 , 0x02 , 0x01 , 0x00 // ~
};


/*************************************************************/
/* Function definitions */
/*************************************************************/

/**************************************/
/* Shifting SPI bit out */
/**************************************/
void NokiaLCD::shiftBits(byte b)
{
	CLK0
	if ((b&128)!=0) SDA1 else SDA0
		CLK1
		
		CLK0
		if ((b&64)!=0) SDA1 else SDA0
			CLK1
			
			CLK0
			if ((b&32)!=0) SDA1 else SDA0
				CLK1
				
				CLK0
				if ((b&16)!=0) SDA1 else SDA0
					CLK1
					
					CLK0
					if ((b&8)!=0) SDA1 else SDA0
						CLK1
						
						CLK0
						if ((b&4)!=0) SDA1 else SDA0
							CLK1
							
							CLK0
							if ((b&2)!=0) SDA1 else SDA0
								CLK1
								
								CLK0
								if ((b&1)!=0) SDA1 else SDA0
									CLK1
									}

/**************************************/
/* Put pixel to LCD */
/**************************************/

void NokiaLCD::LCD_put_pixel(word color, byte x, byte y)
{
	sendCMD(CASET); // page start/end ram
	sendData(x); // for some reason starts at 2
	sendData(x+1);
	
	sendCMD(PASET); // column start/end ram
	sendData(y);
	sendData(y+1);
	sendCMD(RAMWR);
	sendData(color);
}

/**************************************/
/* Sending data */
/**************************************/
void NokiaLCD::sendData(byte data) {
	CLK1
	CS1
	CS0
	
	CLK0
	SDA1
	CLK1
	
	shiftBits(data);
}

/**************************************/
/* Sending command */
/**************************************/
void NokiaLCD::sendCMD(byte data) {
	
	CLK1
	CS1
	CS0
	
	CLK0
	SDA0
	CLK1
	
	shiftBits(data);
	
}

void NokiaLCD::lcd_set_box(byte x1, byte y1, byte x2, byte y2)
{
	sendCMD(CASET); // page start/end ram
	sendData(x1); // for some reason starts at 2
	sendData(x2);
	
	sendCMD(PASET); // column start/end ram
	sendData(y1);
	sendData(y2);
}

void NokiaLCD::lcd_clear(word color, byte x1, byte y1, byte x2, byte y2)
{
	lcd_set_box(x1, y1, x2, y2);
	sendCMD(RAMWR);
	for(unsigned int i = 0; i < (unsigned int)(((x2-x1+1)*(y2-y1+1)) / 2); i++)
	{
		sendData((color >> 4) & 0xFF);
		sendData(((color & 0xF) << 4) | ((color >> 8) & 0xF));
		sendData(color & 0xFF);
	}
}



void NokiaLCD::lcd_init()
{
	// Hardware Reset LCD
//	RESET0
//	delay(10);
//	RESET1
	
	DDRB |= _BV(PB4) | _BV(PB5) | _BV(PB7);
	PORTB |= _BV(PB4) | _BV(PB5) | _BV(PB7);
	
	// Initial state
	CS1
	SDA1
	CLK1
	
	// Display control
	sendCMD(DISCTL);
	sendData(0x03); // 2 divisions, Field swithcing period
	sendData(32); // 132 lines to be display
	sendData(12); // Inversely hightlighted lines - 1
	sendData(0);
	
	sendCMD(COMSCN); // comscn
	sendData(0x01);
	
	sendCMD(OSCON); // oscon
	
	sendCMD(SLPOUT); // sleep out
	
	sendCMD(VOLCTR); // electronic volume, this is kinda contrast/brightness
	sendData(0x24); // this might be different for individual LCDs
	sendData(0x03);
	
	sendCMD(PWRCTR); // power ctrl
	sendData(0x0f); // everything on, no external reference resistors
	delay(100);
	
	sendCMD(DISINV); // display mode
	
	/* sendCMD(DATCTL); // datctl
	 sendData(0x00);
	 sendData(0);
	 sendData(0x01);
	 sendData(0);*/
	
	//Data Control
	sendCMD(DATCTL);
	sendData(0x00); //0x00 for normal, 0x01 for flip vertical
	sendData(0x02); //0x00 for low color grainy, 0x01 for red lines above/below, 0x02 for color, >0x02 for extra blueness
	sendData(0xFF); //does nothing
	
	sendCMD(RGBSET8); // setup color lookup table
	// color table
	
	sendData(0);
	sendData(2);
	sendData(4);
	sendData(6);
	sendData(8);
	sendData(10);
	sendData(12);
	sendData(15);
	// GREEN
	sendData(0);
	sendData(2);
	sendData(4);
	sendData(6);
	sendData(8);
	sendData(10);
	sendData(12);
	sendData(15);
	//BLUE
	sendData(0);
	sendData(4);
	sendData(9);
	sendData(15);
	
	sendCMD(NOP);
	lcd_clear(0xff,0,0,131,131);
	sendCMD(DISON); // display on
	
	//for (i = 0; i < 150; i++){ // this loop adjusts the contrast, change the number of iterations to get
	// sendCMD(VOLUP); // desired contrast. This might be different for individual LCDs
	// delay_ms(2);
	//}
}

void NokiaLCD::draw_color_bar()
{
	lcd_clear(RED,0,0,160,40);
	lcd_clear(GREEN,0,34,160,80);
	lcd_clear(BLUE,0,67,160,120);
	lcd_clear(WHITE,0,100,160,160);
}

void NokiaLCD::draw_text_line(word fcolor, word bcolor,byte x, byte y,char c)
{
	lcd_set_box(x,y,x,y+15);
	sendCMD(RAMWR);
	for(unsigned int i=0;i<8;i++)
	{
		if (1<<i & c)
		{
			sendData((fcolor >> 4) & 0xFF);
			sendData(((fcolor & 0xF) << 4) | ((fcolor >> 8) & 0xF));
			sendData(fcolor & 0xFF);
		}
		else
		{
			sendData((bcolor >> 4) & 0xFF);
			sendData(((bcolor & 0xF) << 4) | ((bcolor >> 8) & 0xF));
			sendData(bcolor & 0xFF);
		}
	}/*
	 
	 unsigned int i;
	 lcd_set_box(x,y,x,y+7);
	 sendCMD(RAMWR);
	 for(i=0;i<8;i++)
	 {
	 if (1<<i & c)
	 sendData(fcolor);
	 else
	 sendData(bcolor);
	 }*/
}


void NokiaLCD::lcd_draw_text(word fcolor, word bcolor, byte x, byte y,char *text)
{
	byte c;
	byte t;
	unsigned int i;
	unsigned int j;
	while(*text != 0)
	{
		t = *text;
		i = t - 32;
		i = i * 5;
		for(j = i; j < i+5; j++)
		{
			c = font[j];
			draw_text_line(fcolor, bcolor, x++, y, c);
		}
		draw_text_line(fcolor, bcolor, x++, y, 0);
		text++;
	}
}


/*
 NokiaLCD.h - Library for a Nokia LCD with the epson driver.
 Created by Thomas Jespersen, July 2009 (Originally Arduino Sketch by Gravitech.us)
 Released into the public domain.
 */
#ifndef NokiaLCD_h
#define NokiaLCD_h

#include "WProgram.h"

class NokiaLCD
{
public:
void LCD_put_pixel(word color, byte x, byte y);
void lcd_set_box(byte x1, byte y1, byte x2, byte y2);
void lcd_clear(word color, byte x1, byte y1, byte x2, byte y2);
void lcd_init();
void draw_color_bar();
void draw_text_line(word fcolor, word bcolor,byte x, byte y,char c);
void lcd_draw_text(word fcolor, word bcolor, byte x, byte y,char *text);

private:
void shiftBits(byte b);
void sendData(byte data);
void sendCMD(byte data);
};

#endif